﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Trippy.Animation;
using Trippy.Abilities;
using Trippy.CollidableGameComponents;
using Trippy.CollidableGameComponents.Events;
using Trippy.CollidableGameComponents.Objects;
using Trippy.Levels;
using Trippy.UI_Overlay;

namespace Trippy.CollidableGameComponents.Agents
{
    public enum IanState { Sitting, Moving }

    public class Ian : Agent
    {
        public static Texture2D SpriteSheet { get; private set; }

        public List<SlaveOrb> Slaves = new List<SlaveOrb>();

        private EventManager eventManager;

        private Random random = new Random();

        private IanState state = IanState.Sitting;

        private Vector2 target;

        public bool Frozen { get; set; }

        private float speed;

        public int HitPoints { get; set; }



        protected override void LoadContent()
        {
            if (SpriteSheet == null) SpriteSheet = CurrentLevel.Game.Content.Load<Texture2D>("Sprites/Agents/ian_original");
            Sprite = new AnimatedSprite(this, SpriteSheet, SpriteSheet.Height, SpriteSheet.Width, 0, 0, -20, -57, 1, 0f);
            foreach (SlaveOrb s in Slaves)
            {
                s.Load();
            }
            base.LoadContent();
        }

        public override void Draw(GameTime gameTime)
        {

            StandardDraw(gameTime);
            foreach (SlaveOrb s in Slaves)
            {
                s.Draw(gameTime);
            }

            base.Draw(gameTime);
        }

        public Ian(Level level)
            : base(level)
        {
            eventManager = new EventManager(level);
            HitPoints = 5;
            Frozen = false;

            for (int i = 0; i < 6; i++)
            {
                SlaveOrb slave = new SlaveOrb(level);
                slave.Scale = new Vector2(.25f, .25f);
                
                slave.Master = this;
                slave.Position = Position + new Vector2(0, 300);
                slave.State = SlaveState.RotateIan;

                slave.HitPoints = 2;

                Slaves.Add(slave);
                slave.MarkForAddition();
            }
            speed = 400f;

            StartAI();
        }


        public void StartAI()
        {
            ScheduleAction();
        }

    

        public override void Update(GameTime gameTime)
        {
            eventManager.Update(gameTime);

            if (Frozen) return;



            float elapsedTime = TrippyUtilities.CalculateElapsedTime(gameTime);
            if (Vector2.DistanceSquared(Position, new Vector2(0f, -700f)) < 490000f)
            {
                IanRetreatFromSpawn();
            }
            
            if (state == IanState.Moving)
            {
                if (Vector2.DistanceSquared(Position, target) < 1000)
                {
                    Position = target;
                    state = IanState.Sitting;
                }
                else
                {
                    Velocity = speed*Vector2.Normalize(target - Position);
                    Position += Velocity * elapsedTime;
                }
                
            }
            else
            {
                Velocity = speed* Vector2.Normalize(new Vector2((float) Math.Cos(gameTime.TotalGameTime.TotalSeconds/5), (float) Math.Sin(gameTime.TotalGameTime.TotalSeconds/5)));
                Position += Velocity * elapsedTime / 10;
            }

            for (int i = 0; i < Slaves.Count; i++)
            {
                // if we're not dead
                if (Slaves.Count == 0) break;

                    Vector2 intendedPosition = Position +
                                               130*
                                               new Vector2(
                                                   (float)
                                                   Math.Cos(MathHelper.TwoPi/Slaves.Count*i +
                                                            gameTime.TotalGameTime.TotalSeconds*6/Slaves.Count + 4),
                                                   (float)
                                                   Math.Sin(MathHelper.TwoPi/ Slaves.Count * i +
                                                            gameTime.TotalGameTime.TotalSeconds*6/Slaves.Count + 4));
                    // if we're rotating
                    if (Slaves[i].State == SlaveState.RotateIan)
                    {
                        Slaves[i].Position = intendedPosition;
                    }
                    if (Slaves[i].State == SlaveState.ReturnIan)
                    {
                        Slaves[i].ChangeTarget(intendedPosition, 500);
                    }


            }

            foreach (SlaveOrb s in Slaves)
            {
                s.Update(gameTime);
            }

            base.Update(gameTime);
        }

        public void DoDamage(int damage)
        {
            HitPoints -= damage;

            Sprite.Tint = new Color(1f, .2f + HitPoints / 7f, .1f + HitPoints / 7f);

            if (HitPoints < 1)
            {
                MarkForDeletion();
            }
        }

        public override void MarkForDeletion()
        {
            Frozen = true;
            foreach (SlaveOrb s in Slaves)
            {
                s.Frozen = true;
            }

            double timeFromNow = 3.0;

            eventManager.Schedule(() => CurrentLevel.Camera.LinearShift(this, 400), 0.0);

            for (int i = 0; i < 300; i++)
            {
                double scheduledTime = timeFromNow;
                double angle = random.NextDouble()*MathHelper.TwoPi;
                double radius = random.NextDouble()*150;
                float x = (float) (radius*Math.Cos(angle));
                float y = (float) (radius*Math.Sin(angle));
                Vector2 blastPosition = new Vector2(x, y) + Position;
                eventManager.Schedule(delegate
                                          {
                                              var effect = new EffectAnimation(CurrentLevel, EffectAnimation.Effects.Explosion, blastPosition);
                                              effect.Scale = new Vector2((float)random.NextDouble()*5);
                                          }, scheduledTime);
                timeFromNow += .01;
            }

            timeFromNow = 0.1;
            for (int i = 0; i < 300; i++)
            {
                double scheduledTime = timeFromNow;
                double angle = random.NextDouble() * MathHelper.TwoPi;
                double radius = random.NextDouble() * 110;
                float x = (float)(radius * Math.Cos(angle));
                float y = (float)(radius * Math.Sin(angle));
                Vector2 blastPosition = new Vector2(x, y) + Position;
                eventManager.Schedule(delegate
                {
                    var effect = new EffectAnimation(CurrentLevel, EffectAnimation.Effects.SmokeBomb, blastPosition);
                    effect.Scale = new Vector2((float)random.NextDouble() * 5);
                }, scheduledTime);
                timeFromNow += .01;
            }


            timeFromNow = 2.8;

            for (int i = 0; i < 600; i++)
            {
                double scheduledTime = timeFromNow;
                double angle = random.NextDouble() * MathHelper.TwoPi;
                double radius = random.NextDouble() * 400;
                float x = (float)(radius * Math.Cos(angle));
                float y = (float)(radius * Math.Sin(angle));
                Vector2 blastPosition = new Vector2(x, y) + Position;
                eventManager.Schedule(delegate
                {
                    var effect = new EffectAnimation(CurrentLevel, EffectAnimation.Effects.Explosion, blastPosition);
                    effect.Scale = new Vector2((float)random.NextDouble() * 10);
                }, scheduledTime);
                timeFromNow += .01;
            }

            for (int i = 0; i < 600; i++)
            {
                double scheduledTime = timeFromNow;
                double angle = random.NextDouble() * MathHelper.TwoPi;
                double radius = random.NextDouble() * 110;
                float x = (float)(radius * Math.Cos(angle));
                float y = (float)(radius * Math.Sin(angle));
                Vector2 blastPosition = new Vector2(x, y) + Position;
                eventManager.Schedule(delegate
                {
                    var effect = new EffectAnimation(CurrentLevel, EffectAnimation.Effects.SmokeBomb, blastPosition);
                    effect.Scale = new Vector2((float)random.NextDouble() * 10);
                }, scheduledTime);
                timeFromNow += .01;
            }

            timeFromNow = 7.7;

            for (int i = 0; i < 600; i++)
            {
                double scheduledTime = timeFromNow;
                double angle = random.NextDouble() * MathHelper.TwoPi;
                double radius = random.NextDouble() * 2400;
                float x = (float)(radius * Math.Cos(angle));
                float y = (float)(radius * Math.Sin(angle));
                Vector2 blastPosition = new Vector2(x, y) + Position;
                eventManager.Schedule(delegate
                {
                    var effect = new EffectAnimation(CurrentLevel, EffectAnimation.Effects.Explosion, blastPosition);
                    effect.Scale = new Vector2((float)random.NextDouble() * 40);
                }, scheduledTime);
                timeFromNow += .01;
            }

            for (int i = 0; i < 600; i++)
            {
                double scheduledTime = timeFromNow;
                double angle = random.NextDouble() * MathHelper.TwoPi;
                double radius = random.NextDouble() * 110;
                float x = (float)(radius * Math.Cos(angle));
                float y = (float)(radius * Math.Sin(angle));
                Vector2 blastPosition = new Vector2(x, y) + Position;
                eventManager.Schedule(delegate
                {
                    var effect = new EffectAnimation(CurrentLevel, EffectAnimation.Effects.SmokeBomb, blastPosition);
                    effect.Scale = new Vector2((float)random.NextDouble() * 40);
                }, scheduledTime);
                timeFromNow += .01;
            }


            eventManager.Schedule(() => CurrentLevel.Camera.LinearZoom(.0f, .3f), 7.7);
            eventManager.Schedule(() => CurrentLevel.Game.Exit(), 12.0);
            

            
        }


        private void ScheduleAction()
        {
            eventManager.Schedule(DetermineAction, 1 + Slaves.Count/3);
        }

        private void DetermineAction()
        {
            ScheduleAction();

            int next = random.Next(0, Slaves.Count + 2);
            switch (state)
            {
                case IanState.Sitting:

                    if (Vector2.DistanceSquared(Position, CurrentLevel.Hero.Position) > 360000)
                    {
                        target = CurrentLevel.Hero.Position - 250 * Vector2.Normalize(CurrentLevel.Hero.Position - Position);
                        
                        state = IanState.Moving;
                    }
                    switch (next)
                    {
                        case 0:
                            IanCharge();
                            break;
                        case 1:
                            IanRetreat();
                            break;
                        case 2:
                            if (Slaves.Count == 0) return;
                            AttackThroughHero();
                            eventManager.Schedule(AttackThroughHero, 1.0);
                            break;
                        case 3:
                            if (Slaves.Count == 0) return;
                            AttackThroughHero();
                            break;
                        case 4:
                            if (Slaves.Count == 0) return;
                            AttackPsychHero();
                            break;
                        case 5:
                            if (Slaves.Count == 0) return;
                            AttackHero();
                            break;
                        case 6:
                            if (Slaves.Count == 0) return;
                            AttackBelowHero();
                            break;
                    }
                    break;
                case IanState.Moving:
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private void IanCharge()
        {
            target = CurrentLevel.Hero.Position;
                        state = IanState.Moving;
        }


        private void IanRetreat()
        {
            target = - 900 * Vector2.Normalize(CurrentLevel.Hero.Position - Position);
                        state = IanState.Moving;
        }

        private void IanRetreatFromSpawn()
        {
            target = -700 * Vector2.Normalize(new Vector2(0f, -500) - Position);
            state = IanState.Moving;
        }

        private void AttackThroughHero()
        {
            if (Slaves.Count == 0) return;

            int selectedSlave = random.Next(0, Slaves.Count);

            Slaves[selectedSlave].ChangeTarget(CurrentLevel.Hero.Position + 100 * Vector2.Normalize(CurrentLevel.Hero.Position - Slaves[selectedSlave].Position), 200 / Slaves.Count + 400);
            Slaves[selectedSlave].State = SlaveState.GoTo;

        }

        private void AttackPsychHero()
        {
            if (Slaves.Count == 0) return;

            int selectedSlave = random.Next(0, Slaves.Count);
            Slaves[selectedSlave].ChangeTarget(CurrentLevel.Hero.Position - 100 * Vector2.Normalize(CurrentLevel.Hero.Position - Slaves[selectedSlave].Position), 200 / Slaves.Count + 400);
            Slaves[selectedSlave].State = SlaveState.GoTo;

        }

        private void AttackHero()
        {
            if (Slaves.Count == 0) return;

            int selectedSlave = random.Next(0, Slaves.Count);
            Slaves[selectedSlave].ChangeTarget(CurrentLevel.Hero.Position, 200 / Slaves.Count + 400);
            Slaves[selectedSlave].State = SlaveState.GoTo;

        }

        private void AttackBelowHero()
        {
            if (Slaves.Count == 0) return;

            int selectedSlave = random.Next(0, Slaves.Count);
            Slaves[selectedSlave].ChangeTarget(CurrentLevel.Hero.Position + new Vector2(0, 75), 200 / Slaves.Count + 400);
            Slaves[selectedSlave].State = SlaveState.GoTo;

        }

        protected override void CollisionHandler(CollidableGameComponent component, CollidableGameComponent.CollisionSide side)
        {
            if (component is Hero)
            {
                Hero h = (Hero)component;
                h.Die(Hero.MannerOfDeath.Enemy);
            }

            base.CollisionHandler(component, side);
        }

    }
}
